<?php

namespace App\Repository\Db;

use App\Repository\Contracts\RepositoryInterface;
use Illuminate\Container\Container;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Validator;
use App\Exceptions\ValidateException;
use App\Repository\Contracts\UserInterface;
use App\Model\School;
use App\Model\ProvinceScore;


/**
 * @Author:: HuangYin
 * @DateTime: 2017/6/7 22:35
 */
abstract class Repository implements RepositoryInterface
{
    /**
     * @var Container
     */
    protected $container;

    /**
     * @var \Illuminate\Database\Eloquent\Builder
     */
    protected $model;

    public function __construct(Container $container)
    {
        $this->container = $container;

        $this->makeModel();
    }

    protected abstract function modelName();

    /**
     * @param array $columns
     * @return mixed
     */
    public function all($columns = ['*'])
    {
        return $this->model->all($columns);
    }

    /**
     * @param int $perPage
     * @param int $currentPage
     * @return \Illuminate\Contracts\Pagination\Paginator
     */
    public function paginate($perPage = 15, $currentPage = 1)
    {
        return $this->model->simplePaginate($perPage, ['*'], 'page', $currentPage);
    }

    /**
     * @param array $data
     * @return mixed
     */
    public function create(array $data)
    {
        return $this->model->create($data);
    }

    /**
     * @param array $data
     * @return bool
     */
    public function save(array $data)
    {
        foreach ($data as $k => $v) {
            if ($v !== NULL) {
                $this->model->$k = $v;
            }
        }

        if ($this->model->save()) {
            return $this->model->id;
        }

        return false;
    }

    /**
     * @param array $data
     * @param       $value
     * @param       $field
     * @return int
     */
    public function update(array $data, $value, $field = '')
    {
        foreach ($data as $k => $v) {
            if ($v === NULL) {
                unset($data[$k]);
            }
        }
        $field = $field ? $field : $this->model->id;

        return $this->model->where($field, $value)->update($data);
    }

    /**
     * @param $id
     * @return mixed
     */
    public function delete($id)
    {
        return $this->model->destroy($id);
    }

    /**
     * @param       $id
     * @param array $columns
     * @return \Illuminate\Database\Eloquent\Collection|Model|null|static|static[]
     */
    public function find($id, $columns = ['*'])
    {
        return $this->model->find($id, $columns);
    }

    /**
     * @param       $field
     * @param       $value
     * @param array $columns
     * @return Model|null|static
     */
    public function findBy($field, $value, $columns = ['*'])
    {
        return $this->model->where($field, '=', $value)->first($columns);
    }

    /**
     * @param       $field
     * @param       $value
     * @param array $columns
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    public function findAllBy($field, $value, $columns = ['*'])
    {
        return $this->model->where($field, '=', $value)->get($columns);
    }

    /**
     * @param       $where
     * @param array $columns
     * @param bool $or
     * @return \Illuminate\Database\Eloquent\Collection|static[]
     */
    public function findAll($where, $columns = ['*'], $or = false)
    {
        $model = $this->model;

        foreach ($where as $field => $value) {
            if ($value instanceof \Closure) {
                $model = (!$or)
                    ? $model->where($value)
                    : $model->orWhere($value);
            } elseif (is_array($value)) {
                if (count($value) === 3) {
                    list($field, $operator, $search) = $value;
                    $model = (!$or)
                        ? $model->where($field, $operator, $search)
                        : $model->orWhere($field, $operator, $search);
                } elseif (count($value) === 2) {
                    list($field, $search) = $value;
                    $model = (!$or)
                        ? $model->where($field, '=', $search)
                        : $model->orWhere($field, '=', $search);
                }
            } else {
                $model = (!$or)
                    ? $model->where($field, '=', $value)
                    : $model->orWhere($field, '=', $value);
            }
        }

        return $model->get($columns);
    }

    /**
     * @author mingren <mingren@naitang.com>
     */
    public function makeModel()
    {
        $modelName = $this->modelName();

        $this->model = new $modelName;
    }

    /**
     * @param $modelName
     * @return Model|mixed
     */
    public function setModel($modelName)
    {
        $model = $this->container->make($modelName);

        if (!$model instanceof Model) {
            throw new \RuntimeException("Class {$model} must be an instance of Illuminate\\Database\\Eloquent\\Model");
        }

        return $this->model = $model;
    }

    /**
     * @param     $id
     * @param     $field
     * @param int $amount
     * @return int
     */
    public function increment($id, $field, $amount = 1)
    {
        $model = $this->model->find($id);

        return $model->increment($field, $amount);
    }

    /**
     * @return mixed
     */
    public function query()
    {
        return $this->model->newQuery();
    }

    /**
     * @param null $action
     * @param array $data
     * @return mixed
     */
    public function getRules($action = null, array $data)
    {
        $rules = $this->rules;
        if (isset($rules[$action])) {
            $rules = $rules[$action];
            if ($data) {
                foreach ($data as $key => $val) {
                    $rules[$key] = $rules[$key] . ',' . $key . ',' . $val;
                }
            }
        }

        return $rules;
    }

    /**
     * @param       $data
     * @param       $action
     * @param array $columns
     * @throws ValidateException
     */
    public function validate($data, $action, $columns = [])
    {
        foreach ($data as $key => $value) {
            if ($value === NULL) {
                unset($data[$key]);
            }
        }

        $validator = Validator::make($data, $this->getRules($action, $columns), $this->messages);

        if ($validator->fails()) {
            throw new ValidateException($validator->messages()->first());
        }
    }

    /**
     * @param $label
     * @param $data
     * @param $total
     * @return array
     */
    protected function simplePaginate($label, $data, $total)
    {
        return [
            $label => $data,
            'total' => $total
        ];
    }

    /**
     * @param      $map
     * @param bool $or
     * @return $this|\Illuminate\Database\Eloquent\Builder|static
     */
    public function searchMap($map, $or = false)
    {
        $model = $this->model;
        if (!is_array($map) || !$map) {
            return $model;
        }

//        $likes = [];
        foreach ($map as $val) {
            if ($val['value'] == null) {
                continue;
            }

//            if ($val['operation'] === 'like') {
//                $likes[] = $val;
//                continue;
//            }

            if ($val['operation'] === 'find_in_set') {
                $model = (!$or)
                    ? $model->whereRaw('FIND_IN_SET(' . $val['value'] . ', ' . $val['field'] . ')')
                    : $model->orWhereRaw('FIND_IN_SET(' . $val['value'] . ', ' . $val['field'] . ')');
            } elseif ($val['operation'] === 'in') {
                $model = (!$or)
                    ? $model->whereIn($val['field'], $val['value'])
                    : $model->orWhereIn($val['field'], $val['value']);
            } else {
                $model = (!$or)
                    ? $model->where($val['field'], $val['operation'], $val['value'])
                    : $model->orWhere($val['field'], $val['operation'], $val['value']);
            }
        }

//        $model = $model->where(function ($query) use ($likes) {
//            foreach ($likes as $like) {
//                $query->orWhere($like['field'], $like['operation'], $like['value']);
//            }
//        });

        return $model;
    }

    /**
     * @param      $modelmap
     * @param bool $or
     * @return $this|\Illuminate\Database\Eloquent\Builder|static
     */
    public function searchModelMap($map)
    {
        $where=[
        ];
        if (!is_array($map) || !$map) {
            return $where;
        }
        foreach ($map as $val) {
            if ($val['value'] == "" || $val['value'] == "%%") {
                continue;
            }
            if ($val['operation'] === '=') {
                $where_in=[$val['field'],$val['operation'],$val['value']];
                array_push($where,$where_in);
            } elseif ($val['operation'] === 'like') {
                $where_in=[$val['field'],$val['operation'],$val['value']];
                array_push($where,$where_in);
            } 
        }
        return $where;
    }
    
}